﻿/*******************************************************************************
Copyright (c) 2011-2013, PKStudio

http://pkstudio.org

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*******************************************************************************/

using System;
using System.Diagnostics;
using System.Drawing;
using System.IO;

namespace PKEngine
{
	/// <summary>
	/// Label alignment enum.
	/// </summary>
	public enum PKLabelAlignment
	{
		Left,
		Center,
		Right,
		Default = Left
	}

	/// <summary>
	/// Interface for labels.
	/// </summary>
	public interface IPKLabel : IPKRgba, IPKDisplayMode
	{
		string Text { get; set; }
	}

	/// <summary>
	/// PKLabelBinFont is a sub class of PKSprite that can render text with specified binary font.
	/// </summary>
	public class PKLabelBinFont : PKSprite, IPKLabel
	{
		#region Private Fields
		private string text;
		private PKLabelAlignment alignment;
		private int charSpacing = 1;
		private int lineSpacing = 3;
		#endregion

		#region Public Properties
		public Size Dimensions { get; private set; }
		public PKBinFont BinFont { get; private set; }

		public string Text
		{
			get
			{
				return this.text;
			}
			set
			{
				if (this.text != value)
				{
					this.text = value;
					this.DrawTextHelper();
				}
			}
		}

		public PKLabelAlignment Alignment
		{
			get
			{
				return this.alignment;
			}
			set
			{
				this.alignment = value;
				this.DrawTextHelper();
			}
		}

		public int CharSpacing
		{
			get
			{
				return this.charSpacing;
			}
			set
			{
				if (value < 0)
				{
					value = 0;
				}
				this.charSpacing = value;
				this.DrawTextHelper();
			}
		}

		public int LineSpacing
		{
			get
			{
				return this.lineSpacing;
			}
			set
			{
				if (value < 0)
				{
					value = 0;
				}
				this.lineSpacing = value;
				this.DrawTextHelper();
			}
		}
		#endregion

		#region Initialization
		public static PKLabelBinFont LabelMake(string text, string fontFile)
		{
			return new PKLabelBinFont(text, fontFile);
		}

		public static PKLabelBinFont LabelMake(string text, string fontFile, PKLabelAlignment alignment)
		{
			return new PKLabelBinFont(text, fontFile, alignment);
		}

		public static PKLabelBinFont LabelMake(string text, string fontFile, PKLabelAlignment alignment, ushort color)
		{
			return new PKLabelBinFont(text, fontFile, alignment, color);
		}

		public static PKLabelBinFont LabelMake(string text, string fontFile, PKLabelAlignment alignment, ushort color, Size dimensions)
		{
			return new PKLabelBinFont(text, fontFile, alignment, color, dimensions);
		}

		public PKLabelBinFont(string text, string fontFile)
			: this(text, fontFile, PKLabelAlignment.Default, PKAlpha.SharedAlpha.ColorWhite, Size.Empty)
		{
		}

		public PKLabelBinFont(string text, string fontFile, PKLabelAlignment alignment)
			: this(text, fontFile, alignment, PKAlpha.SharedAlpha.ColorWhite, Size.Empty)
		{
		}

		public PKLabelBinFont(string text, string fontFile, PKLabelAlignment alignment, ushort color)
			: this(text, fontFile, alignment, color, Size.Empty)
		{
		}

		public PKLabelBinFont(string text, string fontFile, PKLabelAlignment alignment, ushort color, Size dimensions)
			: base()
		{
			Debug.Assert(!string.IsNullOrEmpty(fontFile), "PKLabelBinFont: Argument cannot be null.");
			Debug.Assert(File.Exists(fontFile), "PKLabelBinFont: Cannot find file.");

			this.DisplayMode = PKSpriteDisplayMode.Color;
			this.Color = color;

			this.Dimensions = dimensions;
			this.Rect = Rectangle.Empty;
			this.Alignment = alignment;
			this.CharSpacing = 1;
			this.LineSpacing = 3;
			this.BinFont = PKBinFontCache.SharedFontCache.AddFont(fontFile);
			if (!dimensions.IsEmpty)
			{
				this.Rect = new Rectangle(0, 0, dimensions.Width, dimensions.Height);
				this.Surface = new PKSurface(dimensions);
			}

			// Draw the text.
			this.Text = text;
		}
		#endregion

		#region Private Methods
		private void DrawTextHelper()
		{
			if (this.BinFont != null && !this.BinFont.IsEmpty)
			{
				string[] lines = this.Text.Split(new char[] { '\n' });

				if (this.Dimensions.IsEmpty)
				{
					// Calc the text size
					int maxWidth = 0;
					foreach (string line in lines)
					{
						int width = this.BinFont.MeasureString(line, this.CharSpacing);
						if (width > maxWidth) { maxWidth = width; }
					}
					int maxHeight = lines.Length * (this.BinFont.CommandHeight + this.LineSpacing);
					if (maxHeight > 0)
					{
						maxHeight -= this.LineSpacing;
					}

					if (maxWidth == 0 || maxHeight == 0)
					{
						// If there is no visible text.
						this.Rect = Rectangle.Empty;
						this.Surface = null;
						return;
					}
					else
					{
						// Create a new surface with text size.
						this.Rect = new Rectangle(0, 0, maxWidth, maxHeight);
						this.Surface = new PKSurface(new Size(maxWidth, maxHeight));
						this.Surface.Clear(true);
					}
				}
				else
				{
					// Clear the original text.
					this.Surface.Clear(true);
				}

				Point startPoint = new Point(0, 0);
				foreach (string line in lines)
				{
					switch (this.Alignment)
					{
						case PKLabelAlignment.Left:
							startPoint.X = 0;
							break;
						case PKLabelAlignment.Center:
							startPoint.X = (this.Surface.ContentSize.Width - this.BinFont.MeasureString(line, this.CharSpacing)) / 2;
							break;
						case PKLabelAlignment.Right:
							startPoint.X = this.Surface.ContentSize.Width - this.BinFont.MeasureString(line, this.CharSpacing);
							break;
					}

					startPoint = this.BinFont.DrawString(line, this.Surface, startPoint, this.CharSpacing);

					startPoint.Y += BinFont.CommandHeight + this.LineSpacing;
				}
			}
		}
		#endregion
	}
}
